1{
2 "cells": [
3  {
4   "cell_type": "markdown",
5   "id": "unable-biodiversity",
6   "metadata": {},
7   "source": [
8    "The expression system\n",
9    "===================\n",
10    "\n",
11    "As we saw in the previous section, heyoka.py\n",
12    "needs to be able to represent the right-hand side of an ODE system in symbolic\n",
13    "form in order to be able to compute its high-order derivatives via automatic\n",
14    "differentiation. heyoka.py represents generic mathematical expressions\n",
15    "via a simple [abstract syntax tree (AST)](https://en.wikipedia.org/wiki/Abstract_syntax_tree)\n",
16    "in which the internal nodes are n-ary functions\n",
17    "and the leaf nodes can be:\n",
18    "\n",
19    "- symbolic variables,\n",
20    "- numerical constants,\n",
21    "- runtime parameters.\n",
22    "\n",
23    "Constants and parameters are mathematically equivalent, the only difference being\n",
24    "that the value of a constant is determined when the expression is created, whereas\n",
25    "the value of a parameter is loaded from a user-supplied data array at a later stage.\n",
26    "\n",
27    "As a simple example, here is a graphical representation of the\n",
28    "AST for the expression $\\left( 1 - x^2\\right)y-x$:\n",
29    "\n",
30    "<img src=\"../_static/ast.png\" width=\"350\">\n",
31    "\n",
32    "The construction of the AST of an expression in heyoka.py can be accomplished via natural\n",
33    "mathematical notation:"
34   ]
35  },
36  {
37   "cell_type": "code",
38   "execution_count": 1,
39   "id": "copyrighted-petersburg",
40   "metadata": {},
41   "outputs": [
42    {
43     "name": "stdout",
44     "output_type": "stream",
45     "text": [
46      "The euclidean distance is: sqrt(((square(x) + square(y)) + square(z)))\n"
47     ]
48    }
49   ],
50   "source": [
51    "import heyoka as hy\n",
52    "\n",
53    "# Define the symbolic variables x and y.\n",
54    "x, y = hy.make_vars(\"x\", \"y\")\n",
55    "\n",
56    "# Another way of creating a symbolic variable.\n",
57    "z = hy.expression(\"z\")\n",
58    "\n",
59    "# Create and print an expression.\n",
60    "print(\"The euclidean distance is: {}\".format(hy.sqrt(x*x + y*y + z*z)))"
61   ]
62  },
63  {
64   "cell_type": "markdown",
65   "id": "young-minimum",
66   "metadata": {},
67   "source": [
68    "Numerical constants can be created using any of the floating-point types supported by heyoka.py. For instance, on a typical Linux installation of heyoka.py on an x86 processor, one may write:"
69   ]
70  },
71  {
72   "cell_type": "code",
73   "execution_count": 2,
74   "id": "statewide-embassy",
75   "metadata": {},
76   "outputs": [
77    {
78     "name": "stdout",
79     "output_type": "stream",
80     "text": [
81      "Double-precision 1.1: 1.1000000000000001\n",
82      "Extended-precision 1.1: 1.10000000000000000002\n",
83      "Quadruple-precision 1.1: 1.10000000000000000000000000000000008\n"
84     ]
85    }
86   ],
87   "source": [
88    "print(\"Double-precision 1.1: {}\".format(hy.expression(1.1)))\n",
89    "\n",
90    "import numpy as np\n",
91    "print(\"Extended-precision 1.1: {}\".format(hy.expression(np.longdouble(\"1.1\"))))\n",
92    "\n",
93    "from mpmath import mpf, mp\n",
94    "mp.prec = 113\n",
95    "\n",
96    "print(\"Quadruple-precision 1.1: {}\".format(hy.expression(mpf(\"1.1\"))))"
97   ]
98  },
99  {
100   "cell_type": "markdown",
101   "id": "perfect-conference",
102   "metadata": {},
103   "source": [
104    "Note that, while double precision is always supported in heyoka.py via the ``float`` Python type, support for extended-precision floating-point types varies depending on the software/hardware platform. Specifically:\n",
105    "\n",
106    "- on x86 processors, the NumPy ``longdouble`` type corresponds to 80-bit extended precision on most platforms (the exception being MSVC on Windows, where ``longdouble == float``);\n",
107    "- on some platforms (e.g., Unix ARM 64), the ``longdouble`` type implements the IEEE [quadruple-precision floating-point format](https://en.wikipedia.org/wiki/Quadruple-precision_floating-point_format);\n",
108    "- on some platforms where ``longdouble`` does **not** have quadruple precision, a nonstandard quadruple-precision type called ``__float128`` is instead available in C/C++ (this is the case, for instance, on x86-64 Linux and on some PowerPC platforms). On such platforms, if heyoka.py and the heyoka C++ library were compiled with support for the [mp++ library](https://github.com/bluescarni/mppp) and [mpmath](https://mpmath.org/) is installed, quadruple precision is supported via the ``mpf`` type (provided that the ``mpmath`` working precision is set exactly to 113 bits, as shown above).\n",
109    "\n",
110    "Note that the non-IEEE ``longdouble`` type available on some PowerPC platforms (which implements a double-length floating-point representation with 106 significant bits) is **not** supported by heyoka.py at this time.\n",
111    "\n",
112    "In addition to the standard mathematical operators, heyoka.py's expression system\n",
113    "also supports the following elementary functions (with more to come in the near future):\n",
114    "\n",
115    "* square root,\n",
116    "* exponentiation,\n",
117    "* the basic trigonometric and hyperbolic functions, and their inverse counterparts,\n",
118    "* the natural logarithm and exponential,\n",
119    "* the standard logistic function (sigmoid),\n",
120    "* the error function,\n",
121    "* the inverse of Kepler's elliptic equation."
122   ]
123  },
124  {
125   "cell_type": "code",
126   "execution_count": 3,
127   "id": "accredited-mechanics",
128   "metadata": {},
129   "outputs": [
130    {
131     "data": {
132      "text/plain": [
133       "((cos((x + (2.0000000000000000 * y))) * sqrt(z)) - exp(x))"
134      ]
135     },
136     "execution_count": 3,
137     "metadata": {},
138     "output_type": "execute_result"
139    }
140   ],
141   "source": [
142    "# An expression involving a few elementary functions.\n",
143    "hy.cos(x + 2. * y) * hy.sqrt(z) - hy.exp(x)"
144   ]
145  },
146  {
147   "cell_type": "markdown",
148   "id": "driving-problem",
149   "metadata": {},
150   "source": [
151    "It must be emphasised that heyoka.py's expression system is not a full-fledged\n",
152    "computer algebra system. In particular, its simplification capabilities\n",
153    "are very limited. Because heyoka.py's performance is sensitive\n",
154    "to the complexity of the ODEs, in order to achieve optimal performance\n",
155    "it is important to ensure that\n",
156    "the mathematical expressions supplied to heyoka.py are simplified as\n",
157    "much as possible.\n",
158    "\n",
159    "Note that, starting form version 0.10, heyoka.py's expressions can be converted to/from [SymPy](https://www.sympy.org/en/index.html) expressions.\n",
160    "It is thus possible to use SymPy for the automatic simplifcation of heyoka.py's expressions, and, more generally, to symbolically manipulate\n",
161    "heyoka.py's expressions using the wide array of SymPy's capabilities. See the [SymPy interoperability tutorial](<./sympy_interop.ipynb>)\n",
162    "for a detailed example."
163   ]
164  }
165 ],
166 "metadata": {
167  "kernelspec": {
168   "display_name": "Python 3 (ipykernel)",
169   "language": "python",
170   "name": "python3"
171  },
172  "language_info": {
173   "codemirror_mode": {
174    "name": "ipython",
175    "version": 3
176   },
177   "file_extension": ".py",
178   "mimetype": "text/x-python",
179   "name": "python",
180   "nbconvert_exporter": "python",
181   "pygments_lexer": "ipython3",
182   "version": "3.8.10"
183  }
184 },
185 "nbformat": 4,
186 "nbformat_minor": 5
187}
188